require 'rubygems'
require 'bundler/setup'
require 'posix-spawn'
require 'pathname'
require 'closure_project/command_runner'


module ClosureProject
  module Builders
    class UnitTestBuilder

      DEFAULTS = {
        :dependency_files => [
          'closure/library/closure/goog/base.js',
          'closure/library/closure/goog/deps.js',
          'implsrc/deps.js',
          'src/deps.js'
        ],
        :build_cmd      => 'python closure/library/closure/bin/build/depswriter.py',
        :goog_base      => 'closure/library/closure/goog/',
        :verbose        => false,
        :silent         => false
      }
      
      def initialize options = {}
        @options = DEFAULTS.merge( options );
        
@TestContent = <<HTML
 <html>
   <head>
   </head>
   <body>
   </body>
 </html>
HTML
      end
      
      def extract_provide path
        content = File.read( path );
        reg = /goog\.provide\('([^']+)'\)/
        
        idx1 = content.index('goog.provide(')
        idx2 = content.index(')', idx1 )
        result = content[(idx1+14)...(idx2-1)]

        unless result 
          raise "UnitTestBuilder: No provide could be extracted from #{path}"
        end
        result
      end

      def build test_path, opts = {}
      
        unless File.exists?( test_path ) 
          raise "UnitTestBuilder: '#{test_path}' does not exist"
        end
        
        namespace = extract_provide( test_path );
        
        command = @options[:build_cmd];

        base_filename = test_path.gsub( '_Test.js', '');
        goog_dir = Pathname.new( 'closure/library/closure/goog/' );
        
        Dir[base_filename + '_*.js'].each do |js|
          rel = Pathname.new( js );
          path = rel.relative_path_from( goog_dir ).to_s;
          command += " --path_with_depspath='#{js} #{path}'"    
        end
      
        result = ClosureProject::CommandRunner.run_and_log( command, {
          :verbose => @options[:verbose],
          :silent  => @options[:silent]
        });
        
        
        builder = Nokogiri::HTML( @TestContent );
        test_dir = File.dirname( test_path );
        add_script_tag 'closure/library/closure/goog/base.js', test_dir, builder
        add_script_tag 'closure/library/closure/goog/deps.js', test_dir, builder
        add_script_tag 'implsrc/deps.js', test_dir, builder
        add_script_tag 'src/deps.js', test_dir, builder
        add_script_content_tag result.out, builder, 'head';
        add_script_content_tag "goog.require('#{namespace}')", builder, 'body';
        
        File.open( base_filename + '_Test.html', 'w' ) do |f|
          f.write builder.to_html
        end
        
        return;
        
        # check if there is a _Test.js file at test_path.
        # get goog.provide() statement from test_file.
        # find dependencies for all related files FileName_*.js
        # churn out .html file.
        
        
      
      
        if !opts[:output_file]
          raise "GssBuilder option :output_file not specified";
        end
        
        if opts[:output_file][(-4)..(-1)] != '.css'
          raise 'GssBuilder opts[:output_file] does not end with .css'
        end
        
        output_path = Pathname.new( opts[:output_file] ).expand_path
        
        map_default = output_path.to_s.gsub( '.css', '.map.js' );
        map_path = Pathname.new( opts[:map_file] || map_default ).expand_path
        
        puts "GssBuilder: building in #{Dir.pwd}... "
        puts "  [source] #{@source_path.to_s}"
        puts "  [output] #{output_path.to_s}"
        puts "  [map] #{map_path}"
        puts "  [images] #{@image_path.to_s}"
        
        cmd = @options[:build_cmd]
        if @options[:production]
          cmd += ' --output-renaming-map-format CLOSURE_COMPILED'
          cmd += ' --rename CLOSURE'
        else
          cmd += ' --pretty-print'
          cmd += ' --output-renaming-map-format CLOSURE_UNCOMPILED'
        end
        
        cmd += " --output-renaming-map #{map_path.to_s}"
      
        css_names = custom_css_function_names;
        
        css_names.each do |function_name|
          cmd += " --allowed-non-standard-function #{function_name}"
        end
        
        cmd += ' '+ select_files( names ).join(' ')
      
        result = ClosureProject::CommandRunner.run_and_log( cmd, {
          :verbose => @options[:verbose],
          :silent  => @options[:silent]
        });
        
        content = result.out;
        
        css_names.each do |function_name|
          content.gsub!( /#{function_name}\([^\)]*\)/  ) do |string|
            parse_function(function_name, string, opts )
          end
        end
        
        
        dir = File.dirname(opts[:output_file])
        FileUtils.mkdir_p dir;
        
        File.open(opts[:output_file], 'w' ) do |file|
          file.write( content )
        end
        
      end
      
      private
      
      def parse_function name, content, opts = {}
        case name
          when 'image-url'
            image_url_regex = /image-url\(([^\)]*)\)/
            content.gsub( image_url_regex ) do |pattern|
              data = pattern.match( image_url_regex );
              "url('#{parse_image_url( data[1].gsub(/'|"/, '').strip, opts )}')"
            end
          else
            content
        end
      end
      
      def parse_image_url url, opts = {}
        path = Pathname.new( url );
        if path.relative?
        
          output_file = opts[:output_file];
          output_dir  = File.dirname( output_file )
          out = Pathname.new( output_dir ).expand_path;
          
          rel = @image_path.relative_path_from( out ).to_s;    
          File.join( rel, path.to_s );
        else
          warn "  Cannot set relative path for image-url(#{path.to_s})."
          path.to_s
        end
      end
      
      
      def custom_css_function_names
        [ 'image-url', 'color-stop' ]
      end
      
      def select_files names = []
        names.map do |name|
          path = resolve( name )
          if File.exists? path
            path
          else
            warn "GssBuilder: could not find #{path}"
            nil
          end
        end.compact
      end
    end
  end
end

